/**
 * 
 */
package org.jenkon.commons.collection;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.jenkon.commons.collection.iterator.CompositeIterator;

/**
 * Composite implementation of {@link Collection}.
 * 
 * @author Jens Konopka
 * 
 * @param <E> the type of {@link Collection} value objects.
 */
public class CompositeCollection<E> implements Collection<E> {

	private final Collection<Collection<E>> collections = new HashSet<Collection<E>>();

	/**
	 * @param collection a {@link Collection} to add to the composite.
	 */
	public void addCollection(final Collection<E> collection) {
		collections.add(collection);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#size()
	 */
	public int size() {
		int size = 0;
		for (final Collection<E> collection : collections) {
			size += collection.size();
		}
		return size;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#isEmpty()
	 */
	public boolean isEmpty() {
		for (final Collection<E> collection : collections) {
			if (!collection.isEmpty()) {
				return false;
			}
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#contains(java.lang.Object)
	 */
	public boolean contains(final Object o) {
		for (final Collection<E> collection : collections) {
			if (collection.contains(o)) {
				return true;
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#iterator()
	 */
	public Iterator<E> iterator() {
		final CompositeIterator<E> iterator = new CompositeIterator<E>();
		for (final Collection<E> collection : collections) {
			iterator.addIterator(collection.iterator());
		}
		return iterator;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#toArray()
	 */
	public Object[] toArray() {
		final Object[] result = new Object[this.size()];
		int i = 0;
		for (final Iterator<E> it = this.iterator(); it.hasNext(); i++) {
			result[i] = it.next();
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#toArray(T[])
	 */
	public <T> T[] toArray(final T[] a) {
		throw new UnsupportedOperationException("the toArray(T[]) method is not supported.");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#add(java.lang.Object)
	 */
	public boolean add(final E e) {
		throw new UnsupportedOperationException("the add() method is not supported.");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#remove(java.lang.Object)
	 */
	public boolean remove(final Object o) {
		for (final Collection<E> collection : collections) {
			if (collection.contains(o)) {
				return collection.remove(o);
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#containsAll(java.util.Collection)
	 */
	public boolean containsAll(final Collection<?> c) {
		for (final Collection<E> collection : collections) {
			if (collection.containsAll(c)) {
				return true;
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#addAll(java.util.Collection)
	 */
	public boolean addAll(final Collection<? extends E> c) {
		throw new UnsupportedOperationException("the addAll() method is not supported.");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#removeAll(java.util.Collection)
	 */
	public boolean removeAll(final Collection<?> c) {
		for (final Collection<E> collection : collections) {
			if (collection.containsAll(c)) {
				return collection.removeAll(c);
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#retainAll(java.util.Collection)
	 */
	public boolean retainAll(final Collection<?> c) {
		for (final Collection<E> collection : collections) {
			if (collection.containsAll(c)) {
				return collection.retainAll(c);
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Collection#clear()
	 */
	public void clear() {
		for (final Collection<E> collection : collections) {
			collection.clear();
		}
	}

}
